package ithink.link.service;

import com.alibaba.fastjson.JSONObject;
import ithink.link.base.message.LinkMessage;
import ithink.link.ConfigHelper;
import ithink.link.bean.DeviceInfo;
import ithink.link.exception.ParameterErrorException;
import ithink.link.stack.IConnection;
import ithink.link.stack.LinkConnection;
import ithink.link.stack.LinkStack;
import ithink.link.stack.MqConnection;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

/**
 * Created by zaqb on 17-9-12.
 */
public class LinkService implements LinkStack.IListener{
    private LinkStack userStack = null;
    private MqConnection mqConnection = null;
    private boolean started = false;
    private String name = null;
    private String version = null;
   // private LinkServerHelper serverHelper = null;
    private static final Logger LOGGER = LoggerFactory.getLogger(LinkService.class);
    private DeivceManager deivceManager = null;
    private WebManager webManager = null;




    public LinkService(String name, String version) throws IOException {
        this.name = name;
        this.version = version;
    }


    public synchronized void start() throws Exception {
        if (started) {
            throw new Exception("service has started,name " + this.name);
        }
        try {
            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
                @Override
                public void run() {
                    stop(null);
                }
            }));
            userStack = new LinkStack( "user" );
            userStack.setListener( this );
            userStack.init(new LinkStack.Config());

            mqConnection = new MqConnection(this);

            userStack.setFixedKey( ConfigHelper.getKey() );

            userStack.start( ConfigHelper.getUserPort());

            deivceManager = new DeivceManager( name );
            webManager = new WebManager(mqConnection);

            this.wait();
        } catch (Exception ex) {
            stop(ex);
            throw ex;
        }

    }


    private synchronized void stop(Exception ex) {
        if (ex != null) {
            LOGGER.error("service {} stop because of exception {}", name, ex);
        } else {
            LOGGER.info("service {} stop", name);
        }
        started = false;
        userStack.stop();
        this.notify();
    }

    @Override
    public void onConnected(IConnection connection) {

    }

    @Override
    public void onDisconnected(IConnection connection) {
        deivceManager.deviceLogout( connection );
        if(connection.getContext() != null) {
            webManager.deviceLogout( (String) connection.getContext() );
        }

    }


    @Override
    public void onMessageReceived(IConnection connection, LinkMessage message) {
        String type = message.getText().getString( "type" );
        LOGGER.info( "recv msg: {} from connection:{}:{}", message.getText().toJSONString(),
                connection.remoteIp(), connection.remotePort());
        if(StringUtils.isBlank( type )) {
            LOGGER.error( "null type" );
            return;
        }
        try {
            switch (type) {
                case "login": {
                    String sid = message.getText().getString( "sid" );
                    deivceManager.deviceLogin( connection, message );
                    webManager.deviceLogin(sid);
                    break;
                }
                case "heartbeat": {
                    connection.sendMessage( message );
                    deivceManager.heartbeat( connection );
                    break;
                }
                case "ask-online": {
                    String sid = message.getText().getString( "sid" );
                    DeviceInfo info = deivceManager.getDeviceInfo( sid );
                    message.getText().put( "tag", message.getText().getString( "tag" ) );
                    if (info == null) {
                        message.getText().put( "ret", "1" );
                        message.getText().put( "note", "online" );
                    } else {
                        message.getText().put( "ret", "0" );
                        message.getText().put( "note", "not-online" );
                    }
                    connection.sendMessage( message );
                    break;
                }
                case "set-key":{
                    String key = message.getText().getString( "key" );
                    if(StringUtils.isNotBlank(key)) {
                        userStack.setFixedKey(key);
                    }
                    break;
                }
                case "request-key":{
                    message.getText().put("key", userStack.getFixedKey());
                    connection.sendMessage( message );
                    break;
                }
                case "request-video": {
                    requstVideo( connection, message );
                    break;
                }
                case "port-mapping": {
                    deivceManager.checkPortMapping( connection, message );
                    break;
                }
                case "unregister-device" : {
                    String sid = message.getText().getString( "sid" );
                    deivceManager.deleteDevice( sid );
                }
                case "delete-ctl": {
                    //need not wait for respon
                    transitMessage( connection, message, true );
                    break;
                }
                case "down-alarmvideo":
                default:
                    transitMessage(connection, message, false);
            }
        }catch (ParameterErrorException ex) {
            LOGGER.error( "ex: ", ex );
            connection.disconnect( ex );
        }
    }

    @Override
    public void onMessageSendFailed(IConnection connection, LinkMessage message) {

    }


    private void requstVideo(IConnection connection, LinkMessage message) {
        if(connection == mqConnection) {
            String sid = message.getText().getString( "sid" );
            DeviceInfo info = deivceManager.getDeviceInfo(sid);
            if(info == null) {
                notOnlineResponse( connection, message );
                return;
            }
            if(!"2".equals( message.getText().getString( "video-playback" ) )) {
                //alarm playback need wait for device anser
                //other reply
                connection.sendMessage(message);
            }
            //notice device
            message.getText().put("cmd", "run");
            message.getText().put("dowhat", getDowhat(info, message));
            message.getText().put("hole-ip", message.getText().getString("nat-ip"));
            message.getText().put("hole-port", message.getText().getString("nat-port"));
            message.getText().put("clt-umac", message.getText().getString("umac"));
            transitMessage( connection, message, false);
        }else {
            webManager.response(message);
        }
    }


    LinkMessage getRequestVideoAnswerFowWeb(DeviceInfo info, LinkMessage message) {
        JSONObject text = new JSONObject( );
        String dowhat = getDowhat(info, message);
        text.put("dowhat", dowhat);
        text.put("dev-port-udp", info.getUdpPort());
        text.put("dev-port-tcp", info.getTcpPort());
        text.put("dev-innerip", info.getInnerIp());
        text.put("dev-outerip", info.getExtIp());
        text.put("dowhat", dowhat);
        text.put("ret", "1");
        text.put("note", "success");

        LinkMessage newMessage = new LinkMessage();
        newMessage.setFlag(LinkMessage.FLAG_PART_KEY);
        newMessage.setText( text );
        return newMessage;

    }

    private String getDowhat(DeviceInfo info, LinkMessage message) {
        String cltEip = message.getText().getString( "clt-eip" );
        String netStat = message.getText().getString( "net-stat" );
        if("xxx.xxx.xxx.xxx".equals( cltEip )) {
            //业务要求走中专
            return Defines.TRANSER_TCP;
        }
        if(Defines.SIM_CHANNEL_G.equals( netStat )
                || Defines.SIM_CHANNEL_E.equals( netStat )) {
            return Defines.TRANSER_TCP;
        }
        if(info.getExtIp().equals(cltEip)) {
            return Defines.SAME_NAT_TCP;
        }
        if("1".equals(info.getIsPortMapping())) {
            return Defines.PORT_MAP_TCP;
        }
        return Defines.TRANSER_TCP;

    }

    private void transitMessage(IConnection connection, LinkMessage message, boolean sendOnly) {
        if(connection == mqConnection) {
            //change tag to web
            message.getText().put( "tag", message.getText().getString( "sender" ) );
            String sid = message.getText().getString( "sid" );
            if(StringUtils.isBlank( sid)) {
                LOGGER.error( "no sid", message.getText().toJSONString() );
                return;
            }
            DeviceInfo info = deivceManager.getDeviceInfo( sid );
            if(info == null) {
                //not online
                notOnlineResponse(connection, message);
            }else {
                if(!sendOnly) {
                    webManager.registerTransaction( connection, message );
                }
                info.getConnection().sendMessage( message );
                LOGGER.debug( "send message {} to device:{}", message.getText().toJSONString(), sid );
            }
        }else {
            try {
                LOGGER.debug("send response {} to web:{}", message.getText().toJSONString(),WebManager.getTransactionKey(message));
                webManager.response(message);
            } catch (ParameterErrorException e) {
                LOGGER.error("", e);
            }

        }
    }

    private void notOnlineResponse(IConnection connection, LinkMessage message) {
        message.getText().put( "tag", message.getText().getString( "sender" ) );
        message.getText().put( "ret", 0 );
        message.getText().put( "note", "not-online" );
        LOGGER.debug( "not online, {}", message.getText().toJSONString() );
        connection.sendMessage( message );

    }


}
